﻿using Devil;
using Devil.Persistent;
using GameToolkit.BehaviourTree;
using LitJson;
using System.Collections;
using System.Collections.Generic;
using System.Threading;
using UnityEngine;

namespace GameToolkit
{
    sealed public class TaskGraph : System.IDisposable, ICommandHandler, IJsonSerializable, IBeforeDeserializeScheduler, IReferencedPersistent, ISharedInstance, IValidator
    {
        public event System.Action<TaskGraph> OnStateChanged;

        internal class Entity
        {
            internal bool isWarmup;
            internal bool isEnter;
            internal TaskNode node;
            internal TaskEntity entity;
            internal List<Flow> flows;
        }

        public enum EFlowType
        {
            Run,
            Kill,
        }

        public struct Flow
        {
            internal Entity next;
            public EFlowType type;
            public int result;

            public TaskEntity nextEntity => next == null ? null : next.entity;
            
            public bool IsTrue(TaskEntity from)
            {
                if (result == (int)EReservedTaskResult.Always)
                    return from.State != ETaskState.Processing;
                else if (result == (int)EReservedTaskResult.Finish)
                    return from.State == ETaskState.Complete && from.ResultValue != (int)EReservedTaskResult.GiveUp;
                else
                    return from.State == ETaskState.Complete && result == from.ResultValue;
            }
        }

        enum EPlayState
        {
            None,
            Start,
            Playing,
            Destroied,
        }

        string mName;
        string mAssetPath;
        TaskGraphAsset mAsset;
        IAssetPtr mAssetPtr;
        int mState;
        int mResult;
        public ETaskState State
        {
            get
            {
                if (mState == 0)
                    return mPlayState == EPlayState.Playing || mPlayState == EPlayState.Start ? ETaskState.Processing : ETaskState.Inactive;
                else
                    return (ETaskState)mState;
            }
        }
        public int ResultValue => mResult;

        object mOwner;

        object mLock;
        int mId;
        TaskGraph mParent;
        HashSet<TaskGraph> mChildren;
        Entity mStartEntity;
        Dictionary<string, Entity> mAllEntities; // <guid, entity>
        RandomList<Entity> mActiveEntities;
        ParallelDispatcher.Handler mAsyncAction;
        bool mIsDirtyGraph;
        int mInitialized;
        EPlayState mPlayState;
        public int InstanceId => mId;
        public string name
        {
            get { return mName; }
            set
            {
                mName = value;
            }
        }
        public TaskGraphAsset asset => mAsset;
        public TaskGraph parent => mParent;
        public bool IsValid => mPlayState != EPlayState.Destroied;

        public void SetOwner(object owner)
        {
            if (owner != null && owner.GetType().IsValueType)
                throw new System.ArgumentException("The graph's owner type don't support value types.");
            mOwner = owner;
        }

        public T GetOwner<T>() where T : class
        {
            var owner = mOwner as T;
            if (owner == null)
            {
                lock (mLock)
                {
                    var supper = mParent;
                    while (supper != null && owner == null)
                    {
                        owner = supper.mOwner as T;
                        supper = supper.mParent;
                    }
                }
            }
            return owner;
        }

        void SetState(ETaskState state, int result)
        {
            var stat = Interlocked.Exchange(ref mState, (int)state);
            var value = state == ETaskState.Complete ? Interlocked.Exchange(ref mResult, result) : result;
            if (stat != (int)state || value != result)
            {
#if UNITY_EDITOR
                if (mAsset != null)
                    Debug.Log($"The graph ({mAsset.Title}) state: {State} (result: {(EReservedTaskResult)mResult})");
#endif
                OnStateChanged?.Invoke(this);
            }
        }

        public TaskGraph(TaskGraphAsset graphAsset, int id = 0)
        {
            mLock = new object();
            mId = id == 0 ? GlobalUtil.GenUniqueID() : id;
            mName = "root";
            mState = 0;
            mAsset = graphAsset;
            //CreateNodes(graphAsset);
        }

        public TaskGraph(string graphAsset, int id = 0)
        {
            mLock = new object();
            mId = id == 0 ? GlobalUtil.GenUniqueID() : id;
            mAssetPath = graphAsset;
            mName = "root";
            mState = 0;
            mAssetPtr = AssetsUtil.GetAssetAsync<TaskGraphAsset>(graphAsset);
            if (mAssetPtr != null)
            {
                mAssetPtr.Retain();
            }
        }

        private TaskGraph(object syncLock, TaskGraphAsset graphAsset, int id)
        {
            mLock = syncLock;
            mId = id == 0 ? GlobalUtil.GenUniqueID() : id;
            mState = 0;
            mAsset = graphAsset;
            //CreateNodes(graphAsset);
        }

        private TaskGraph(object syncLock, string graphAsset, int id)
        {
            mLock = syncLock;
            mId = id == 0 ? GlobalUtil.GenUniqueID() : id;
            mAssetPath = graphAsset;
            mState = 0;
            mAssetPtr = AssetsUtil.GetAssetAsync<TaskGraphAsset>(graphAsset);
            if (mAssetPtr != null)
            {
                mAssetPtr.Retain();
            }
        }

        public TaskGraph NewSubGraph(string graphAsset, int id = 0)
        {
            lock (mLock)
            {
                if (mPlayState == EPlayState.Destroied)
                    return null;
                var graph = new TaskGraph(mLock, graphAsset, id);
                graph.mParent = this;
#if UNITY_EDITOR
                var depth = 1;
                var root = this;
                while (root.parent != null)
                {
                    depth++;
                    root = root.parent;
                }
                graph.mName = $"{root.name}/sub[{depth}]";
#else
                graph.mName = mName;
#endif
                if (mChildren == null)
                    mChildren = new HashSet<TaskGraph>();
                mChildren.Add(graph);
                return graph;
            }
        }

        public TaskGraph NewSubGraph(TaskGraphAsset graphAsset, int id = 0)
        {
            lock (mLock)
            {
                if (mPlayState == EPlayState.Destroied)
                    return null;
                var graph = new TaskGraph(mLock, graphAsset, id);
                graph.mParent = this;
#if UNITY_EDITOR
                var depth = 1;
                var root = this;
                while (root.parent != null)
                {
                    depth++;
                    root = root.parent;
                }
                graph.mName = $"{root.name}/sub[{depth}]";
#else
                graph.mName = mName;
#endif
                if (mChildren == null)
                    mChildren = new HashSet<TaskGraph>();
                mChildren.Add(graph);
                return graph;
            }
        }

        public TaskEntity GetEntity(TaskNode node)
        {
            Entity ent;
            if (node != null && mAllEntities.TryGetValue(node.guid, out ent))
                return ent.entity;
            else
                return null;
        }

        public void GetNextFlows(TaskNode node, ICollection<Flow> nextFlows, FilterDelegate<Flow> filter = null)
        {
            Entity ent;
            if (nextFlows != null && node != null && mAllEntities.TryGetValue(node.guid, out ent))
            {
                var len = ent.flows == null ? 0 : ent.flows.Count;
                for (int i = 0; i < len; i++)
                {
                    if (filter == null || filter(ent.flows[i]))
                        nextFlows.Add(ent.flows[i]);
                }
            }
        }

        public ParallelDispatcher.Handler GetHandler()
        {
            return mAssetPtr.GetParallelHandler();
        }

        public T GetActiveEntity<T>(bool getFromChildren = false) where T : class
        {
            lock (mLock)
            {
                var active = mActiveEntities;
                if (active != null)
                {
                    for (int i = active.Count - 1; i >= 0; i--)
                    {
                        if (active[i].entity is T t)
                            return t;
                    }
                }
                if (getFromChildren)
                {
                    var lst = CachedList<TaskGraph>.GetList();
                    var index = 0;
                    lst.Add(this);
                    while (index < lst.Count)
                    {
                        var graph = lst[index++];
                        var children = graph.mChildren;
                        if (children != null)
                        {
                            foreach (var item in children)
                            {
                                var subactive = item.mActiveEntities;
                                if (subactive != null)
                                {
                                    for (int i = subactive.Count - 1; i >= 0; i--)
                                    {
                                        if (subactive[i].entity is T t)
                                        {
                                            CachedList<TaskGraph>.Release(lst);
                                            return t;
                                        }
                                    }
                                }
                                lst.Add(item);
                            }
                        }
                    }

                    CachedList<TaskGraph>.Release(lst);
                }
            }
            return default;
        }

        public void GetAllEntities(ICollection<TaskEntity> entities, bool includeChildren, FilterDelegate<TaskEntity> filter = null)
        {
            if (entities == null)
                return;
            lock (mLock)
            {
                if (includeChildren)
                {
                    var lst = CachedList<TaskGraph>.GetList();
                    lst.Add(this);
                    var index = 0;
                    while (index < lst.Count)
                    {
                        var graph = lst[index++];
                        var ents = graph.mAllEntities;
                        if (ents != null)
                        {
                            foreach (var it in ents.Values)
                            {
                                if (it.entity != null && (filter == null || filter(it.entity)))
                                    entities.Add(it.entity);
                            }
                        }
                        var children = graph.mChildren;
                        if (children != null)
                        {
                            foreach (var child in children)
                            {
                                lst.Add(child);
                            }
                        }
                    }
                    CachedList<TaskGraph>.Release(lst);
                }
                else
                {
                    var ents = mAllEntities;
                    if (ents != null)
                    {
                        foreach (var it in ents.Values)
                        {
                            if (it.entity != null && (filter == null || filter(it.entity)))
                                entities.Add(it.entity);
                        }
                    }
                }
            }
        }

        IEnumerator CreateNodes()
        {
            TaskGraphAsset graphAsset;
            if (mAssetPtr != null)
            {
                yield return mAssetPtr.GetParallelHandler();
                graphAsset = mAssetPtr.Asset as TaskGraphAsset;
            }
            else
            {
                graphAsset = mAsset;
            }
            var size = graphAsset == null ? 0 : graphAsset.NodesCount;
            if (size > 0)
            {
                var allEntities = new Dictionary<string, Entity>(Mathf.Max(4, graphAsset.TaskCount));
                var startNode = graphAsset.StartNode;
                Entity startEntity = null;
                for (int i = 0; i < size; i++)
                {
                    var nodeAsset = graphAsset[i];
                    if (nodeAsset is TaskNode tnode)
                    {
                        var entity = tnode.Create();
                        if (entity == null && tnode is not FinalTaskNode)
                            continue;
                        var inst = new Entity();
                        inst.node = tnode;
                        inst.entity = entity;
                        allEntities[tnode.guid] = inst;
                        if (startNode == nodeAsset)
                            startEntity = inst;
                    }
                }
                InitFlows(allEntities);
                lock (mLock)
                {
                    if (mPlayState != EPlayState.Destroied)
                    {
                        mAsset = graphAsset;
                        mAllEntities = allEntities;
                        mStartEntity = startEntity;
                        mActiveEntities = new RandomList<Entity>(Mathf.Max(graphAsset.ActiveBufferSize, 4));
                    }
                }
                InvokeCreateNode();
                //var job = ParallelUtils.ScheduleParallel<Entity>(InvokeCreateNode, allEntities.Values);
#if UNITY_EDITOR
                var job = ParallelUtils.Schedule(() => mAsset.AddGraph(this), false/*, job*/);
#endif
                yield return job;
            }
        }

        void InitFlows(Dictionary<string, Entity> allEntities)
        {
            foreach (var entity in allEntities.Values)
            {
                for (int i = entity.node.PreconditionCount - 1; i >= 0; i--)
                {
                    var prec = entity.node.GetPrecondition(i);
                    Entity node;
                    if (prec.node != null && allEntities.TryGetValue(prec.node.guid, out node))
                    {
                        if (node.flows == null)
                            node.flows = new List<Flow>(node.node.ChildrenBufferSize);
                        Flow flow;
                        flow.result = prec.result;
                        flow.next = entity;
                        flow.type = EFlowType.Run;
                        node.flows.Add(flow);
                    }
                }
                for (int i = entity.node.KillerCount - 1; i >= 0; i--)
                {
                    var prec = entity.node.GetKiller(i);
                    Entity node;
                    if (prec.node != null && allEntities.TryGetValue(prec.node.guid, out node))
                    {
                        if (node.flows == null)
                            node.flows = new List<Flow>(node.node.ChildrenBufferSize);
                        Flow flow;
                        flow.result = prec.result;
                        flow.next = entity;
                        flow.type = EFlowType.Kill;
                        node.flows.Add(flow);
                    }
                }
            }
        }

        void InvokeCreateNode(/*Entity entity*/)
        {
            foreach (var entity in mAllEntities.Values)
            {
                try
                {
                    if (entity.entity != null)
                    {
                        entity.entity.InvokeCreate(this, entity.node);
                        if (!entity.isWarmup && entity.node.WarmupOnCreate)
                        {
                            entity.isWarmup = true;
                            entity.entity.InvokeWarmup(this);
                        }
                    }
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
            }
        }

        public void StartGraph()
        {
            lock (mLock)
            {
                if (mPlayState == EPlayState.None)
                {
                    mPlayState = EPlayState.Start;
                    mIsDirtyGraph = true;
                    OnStateChanged?.Invoke(this);
                }
            }
        }

        public void StopGraph()
        {
            lock (mLock)
            {
                if (mPlayState == EPlayState.Start || mPlayState == EPlayState.Playing)
                {
                    mPlayState = EPlayState.None;
                    var exitLst = CachedList<Entity>.GetList();
                    for (int i = 0; i < mActiveEntities.Count; i++)
                    {
                        exitLst.Add(mActiveEntities[i]);
                    }
                    mActiveEntities.Clear();
                    if (exitLst.Count > 0)
                        mAsyncAction = ParallelUtils.ScheduleParallel<Entity>(ExitEntity, exitLst, mAsyncAction);
                    else
                        CachedList<Entity>.Release(exitLst);
                }
            }
            //for (int i = 0; i < exitLst.Count; i++)
            //{
            //    try
            //    {
            //        if (exitLst[i].isEnter)
            //        {
            //            exitLst[i].isEnter = false;
            //            exitLst[i].entity.InvokeOnExit(this);
            //        }
            //    }
            //    catch (System.Exception e)
            //    {
            //        Debug.LogException(e);
            //    }
            //}
            //
        }

        public void UpdateTaskGraph(float deltaTime)
        {
            if (mPlayState == EPlayState.Destroied || mAsyncAction.IsAlive)
                return;

            var init = Interlocked.CompareExchange(ref mInitialized, 1, 0);
            if (init == 0)
            {
                mAsyncAction = ParallelUtils.Schedule(CreateNodes, false, mAsyncAction);
#if UNITY_EDITOR
                mAsyncAction.SampleExecuteTime(name);
#endif
                return;
            }

            List<ITick> ticks = null;
            var dirty = mIsDirtyGraph;
            mIsDirtyGraph = false;
            List<Entity> enterLst = null, exitLst = null;
            if (dirty)
            {
                enterLst = CachedList<Entity>.GetList();
                exitLst = CachedList<Entity>.GetList();
            }
            UpdateExecutableNodes(dirty, exitLst, enterLst, ref ticks);
            if (dirty)
            {
                ExecuteTasks(exitLst, enterLst, ticks);
                CachedList<Entity>.Release(exitLst);
                CachedList<Entity>.Release(enterLst);
            }
            if (ticks != null)
            {
                for (int i = 0; i < ticks.Count; i++)
                {
                    ticks[i].OnTick(deltaTime);
                }
                CachedList<ITick>.Release(ticks);
            }
        }

        void UpdateExecutableNodes(bool dirty, List<Entity> exitLst, List<Entity> enterLst, ref List<ITick> ticks)
        {
            lock (mLock)
            {
                if (mPlayState == EPlayState.Start)
                {
                    mPlayState = EPlayState.Playing;
                    ExecuteStartNodes(enterLst);
                }
                else if (mPlayState == EPlayState.Playing && mState == (int)NodeState.Running)
                {
                    ticks = CachedList<ITick>.GetList();
                    // exit finished tasks
                    if (dirty)
                    {
                        for (int i = mActiveEntities.Count - 1; i >= 0; i--)
                        {
                            var entity = mActiveEntities[i];
                            if (entity.entity.State != ETaskState.Processing)
                            {
                                mActiveEntities.RemoveAt(i);
                                exitLst.Add(entity);
                                //if (entity.IsFinalTask)
                                //{
                                //    SetState(entity.entity.State);
                                //}
                            }
                        }
                    }
                    // join ticks
                    for (int i = mActiveEntities.Count - 1; i >= 0; i--)
                    {
                        if (mActiveEntities[i].entity is ITick tick)
                            ticks.Add(tick);
                    }
                    // join new tasks (new tasks don't execute ITick during this frame)
                    if (dirty && mState == (int)ETaskState.Processing)
                    {
                        for (int i = exitLst.Count - 1; i >= 0; i--)
                        {
                            var entity = exitLst[i];
                            var flows = entity.flows == null ? 0 : entity.flows.Count;
                            for (int k = 0; k < flows; k++)
                            {
                                var flow = entity.flows[k];
                                if (!flow.IsTrue(entity.entity))
                                    continue;
                                if (flow.type == EFlowType.Run)
                                {
                                    // finish
                                    if (flow.next.entity == null)
                                    {
                                        SetState(ETaskState.Complete, entity.entity.ResultValue);
                                    }
                                    else if (!mActiveEntities.Contains(flow.next))
                                    {
                                        mActiveEntities.Add(flow.next);
                                        enterLst.Add(flow.next);
                                    }
                                }
                                else if (flow.type == EFlowType.Kill && flow.next.entity.State <= ETaskState.Processing)
                                {
                                    mIsDirtyGraph = true;
                                    var ticking = flow.next.entity.State == ETaskState.Processing ? flow.next.entity as ITick : null;
                                    //flow.next.entity.State = NodeState.Failed;
                                    flow.next.entity.GiveUp();
                                    if (ticking != null)
                                        ticks.Remove(ticking);
                                }
                            }
                        }

                        if (mState != (int)ETaskState.Processing)
                        {
                            for (int i = mActiveEntities.Count - 1; i >= 0; i--)
                            {
                                var entity = mActiveEntities[i];
                                exitLst.Add(entity);
                                entity.entity.GiveUp();
                                //if (entity.entity.State == ETaskState.Processing)
                                //entity.entity.State = NodeState.Failed;
                            }
                            mActiveEntities.Clear();
                            enterLst.Clear();
                            if (ticks != null)
                                ticks.Clear();
                        }
                    }

                    if (mState == (int)ETaskState.Processing && mActiveEntities.Count == 0)
                    {
                        SetState(ETaskState.Complete, (int)EReservedTaskResult.Finish);
                    }
                }
            }
        }

        void ExecuteTasks(List<Entity> exitLst, List<Entity> enterLst, List<ITick> ticks)
        {
            for (int i = 0; i < exitLst.Count; i++)
            {
                if (exitLst[i].isEnter)
                {
                    exitLst[i].isEnter = false;
                    try
                    {
                        exitLst[i].entity.InvokeOnExit(this);
                    }
                    catch (System.Exception e)
                    {
                        Debug.LogException(e);
                        mIsDirtyGraph = true;
                    }
                }
            }
            for (int i = 0; i < enterLst.Count; i++)
            {
                if (!enterLst[i].isEnter)
                {
                    enterLst[i].isEnter = true;
                    try
                    {
                        if (!enterLst[i].isWarmup)
                        {
                            enterLst[i].isWarmup = true;
                            enterLst[i].entity.InvokeWarmup(this);
                        }
                        enterLst[i].entity.InvokeOnEnter(this);
                        if (enterLst[i].entity.State != ETaskState.Processing)
                        {
                            mIsDirtyGraph = true;
                            if (ticks != null && enterLst[i].entity is ITick tick)
                                ticks.Remove(tick);
                        }
                        // warmup next tasks
                        else
                        {
                            var flows = enterLst[i].flows == null ? 0 : enterLst[i].flows.Count;
                            for (int k = 0; k < flows; k++)
                            {
                                var flow = enterLst[i].flows[k];
                                if (!flow.next.isWarmup && flow.next.entity != null)
                                {
                                    flow.next.isWarmup = true;
                                    flow.next.entity.InvokeWarmup(this);
                                }
                            }
                        }
                    }
                    catch (System.Exception e)
                    {
                        Debug.LogException(e);
                        mIsDirtyGraph = true;
                    }
                }
            }
        }

        void ExecuteStartNodes(List<Entity> enterLst)
        {
            if (mState == (int)ETaskState.Inactive)
            {
                SetState(ETaskState.Processing, 0);
                if (mStartEntity != null)
                {
                    mActiveEntities.Add(mStartEntity);
                    enterLst.Add(mStartEntity);
                }
            }
            else if (mState == (int)ETaskState.Processing)
            {
                foreach (var entity in mAllEntities.Values)
                {
                    if (entity.entity != null && entity.entity.State == ETaskState.Processing)
                    {
                        mActiveEntities.Add(entity);
                        enterLst.Add(entity);
                    }
                }
            }
        }

        internal void ReportTaskState(TaskEntity task)
        {
            mIsDirtyGraph = true;
        }

        void ExitEntity(Entity entity)
        {
            if (entity.isEnter)
            {
                entity.isEnter = false;
                try
                {
                    entity.entity.InvokeOnExit(this);
                }
                catch (System.Exception e)
                {
                    Debug.LogException(e);
                }
            }
        }

        void ReleaseEntity(Entity entity)
        {
            entity.isWarmup = false;
            try
            {
                if (entity.entity != null)
                    entity.entity.InvokeRelease(this);
            }
            catch (System.Exception e)
            {
                Debug.LogException(e);
            }
        }

        public void Dispose()
        {
            lock (mLock)
            {
                if (mPlayState == EPlayState.Destroied)
                    return;
#if UNITY_EDITOR
                if (mAsset != null)
                    mAsset.RemoveGraph(this);
#endif
                mPlayState = EPlayState.Destroied;
                var all = mAllEntities;
                var active = mActiveEntities;
                var ptr = mAssetPtr;
                var children = mChildren;
                var parent = mParent;
                var job = mAsyncAction;
                mAssetPtr = null;
                mAsset = null;
                mAllEntities = null;
                mActiveEntities = null;
                mChildren = null;
                mParent = null;
                mOwner = null;
                mAsyncAction = default;
                if (parent != null && parent.mChildren != null)
                {
                    parent.mChildren.Remove(this);
                }
                if (children != null)
                {
                    foreach (var child in children)
                    {
                        child.Dispose();
                    }
                }
                if (ptr != null)
                    ptr.Release();
                if (active != null && active.Count > 0)
                    job = ParallelUtils.ScheduleParallel<Entity>(ExitEntity, active, job);
                if (all != null)
                    ParallelUtils.ScheduleParallel<Entity>(ReleaseEntity, all.Values, job);
            }
        }

        #region serialization

        ParallelDispatcher.Handler IBeforeDeserializeScheduler.Schedule(IPersistentMeta meta)
        {
            var init = Interlocked.CompareExchange(ref mInitialized, 1, 0);
            if (init == 0)
            {
                mAsyncAction = ParallelUtils.Schedule(CreateNodes, false, mAsyncAction);
            }
            return mAsyncAction;
        }

        internal void RestoreParentGraph()
        {
            if(mParent != null)
            {
                if (mParent.mChildren == null)
                    mParent.mChildren = new HashSet<TaskGraph>();
                mParent.mChildren.Add(this);
            }
        }

        internal void RestoreSyncLock()
        {
            if (mParent != null)
                return;
            var lst = CachedList<TaskGraph>.GetList();
            lst.Add(this);
            var index = 0;
            var syncLock = mLock;
            lock (syncLock)
            {
                while (index < lst.Count)
                {
                    var graph = lst[index++];
                    if (graph.mChildren != null)
                    {
                        foreach (var child in graph.mChildren)
                        {
                            child.mLock = syncLock;
                            lst.Add(child);
                        }
                    }
                }
            }
            CachedList<TaskGraph>.Release(lst);
        }

        JsonData IJsonSerializable.OnSerialize()
        {
            if (string.IsNullOrEmpty(mAssetPath))
                return null;
            var dt = new JsonData();
            dt["id"] = InstanceId;
            dt["parent"] = PersistentSystem.Instance.SerializeReference(mParent);
            if (mOwner is ISharedInstance sinst)
                dt["owner"] = PersistentSystem.Instance.SerializeReference(sinst);
            dt["asset"] = mAssetPath;
            dt["state"] = mState;
            if (mState == (int)ETaskState.Complete)
            {
                dt["result"] = mResult;
            }
            if (mState != (int)ETaskState.Inactive)
            {
                var allEntities = mAllEntities;
                if (allEntities != null)
                {
                    var nodes = new JsonData();
                    dt["nodes"] = nodes;
                    foreach (var entity in allEntities.Values)
                    {
                        var it = entity.entity;
                        if (it == null || it.State == ETaskState.Inactive || !it.IsPersistent)
                            continue;
                        var obj = new JsonData();
                        obj["state"] = (int)it.State;
                        if (it.State == ETaskState.Complete)
                            obj["result"] = it.ResultValue;
                        if ((entity.node.AlwaysPersistent || it.State == ETaskState.Processing) && it is IJsonSerializable ser)
                            obj["entity"] = ser.OnSerialize();
                        nodes[entity.node.guid] = obj;
                    }
                }
            }
            return dt;
        }

        void IJsonSerializable.OnDeserialize(JsonData content)
        {
            if (content == null || !content.IsObject)
                return;
            //if (!mIsInitialized)
            //{
            //    mIsInitialized = true;
            //    if (mAssetPtr != null)
            //        CreateNodes(mAssetPtr.Asset as TaskGraphAsset, false);
            //    else
            //        CreateNodes(mAsset, false);
            //}
            mParent = PersistentSystem.Instance.DeserializeReference(content["parent"]) as TaskGraph;
            mOwner = PersistentSystem.Instance.DeserializeReference(content["owner"]);
            mState = (int)content["state"];
            if (mState == (int)ETaskState.Complete)
                mResult = (int)content["result"];
            if (mState != (int)ETaskState.Inactive)
            {
                var allEntities = mAllEntities;
                var nodes = content["nodes"];
                Entity ent;
                var activeNum = 0;
                if (allEntities != null && nodes != null)
                {
                    foreach (var guid in nodes.Keys)
                    {
                        if (!allEntities.TryGetValue(guid, out ent) || ent.entity == null)
                            continue;
                        var item = nodes[guid];
                        if (item == null)
                            continue;
                        ent.entity.SetState((ETaskState)(int)item["state"], (int)item["result"]);
                        if ((ent.node.AlwaysPersistent || ent.entity.State == ETaskState.Processing) && ent.entity is IJsonSerializable ser)
                            ser.OnDeserialize(item["entity"]);
                        if (ent.entity.State == ETaskState.Processing)
                            activeNum++;
                    }
                }
                if (activeNum == 0 && mState == (int)ETaskState.Processing)
                    mState = (int)ETaskState.Inactive;
            }
        }

        void IReferencedPersistent.GetReferencedInstances(ICollection<ISharedInstance> objs)
        {
            if (mOwner is ISharedInstance ownerInst)
                objs.Add(ownerInst);
            var allEntities = mAllEntities;
            if (allEntities != null)
            {
                foreach (var ent in allEntities.Values)
                {
                    if (ent.entity is ISharedInstance inst)
                        objs.Add(inst);
                    else if (ent.entity is IReferencedPersistent refinst)
                        refinst.GetReferencedInstances(objs);
                }
            }
        }
        #endregion
    }

}
